<!--
  Copyright: (c) 2016-2020, ST-One Ltda., Guilherme Francescon Cittolin <guilherme@st-one.io>
  GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
-->

<style>
	.eth-ip-vars-prog-title {
		/*min-height: 35px;*/
		background-color: #f3f3f3;
		padding: 5px;
	}
	.eth-ip-vars-prog-handle {
		padding: 5px;
		color: #ddd;
	}
	.eth-ip-vars-prog-chevron {
		margin: 3px 5px;
		width: 10px;
	}
	.node-input-variables-container-row, 
	.node-input-variables-container-row li {
		padding: 0;
	}
	input.eth-ip-vars-var-name {
		width: 50% !important;
	}
	select.eth-ip-vars-var-type {
		width: 40% !important;
		margin-left: 5px;
	}
	.node-input-variables-container-row .red-ui-editableList-container{
		padding: 0;
		min-height: 300px;
	}
	.eth-ip-vars-prog-item .red-ui-editableList-container{
		border-radius: 0;
		border: none;
		height: auto !important;
		min-height: unset;
	}
	.eth-ip-vars-var-list {
		padding: 0 7px;
	}
</style>

<script type="text/html" data-template-name="eth-ip endpoint">
    <div class="form-row">
		<ul style="background: #fff; min-width: 600px; margin-bottom: 20px;" id="node-config-ethip-endpoint-tabs"></ul>
	</div>
	<div id="node-config-ethip-endpoint-tabs-content" style="min-height: 170px; height: 95%">
		<div id="ethip-endpoint-tab-connection" style="display:none">
			<div class="form-row">
				<label for="node-config-input-address"><i class="fa fa-globe"></i> <span data-i18n="ethip.endpoint.label.address"></span></label>
				<input class="input-append-left" type="text" id="node-config-input-address" data-i18n="[placeholder]ethip.endpoint.label.address" style="width: 40%;">
				<label for="node-config-input-slot" style="margin-left: 10px; width: 35px; "> <span data-i18n="ethip.endpoint.label.slot"></span></label>
				<input type="text" id="node-config-input-slot" data-i18n="[placeholder]ethip.endpoint.label.port" style="width: 50px">
			</div>
			<div class="form-row">
				<label for="node-config-input-cycletime"><i class="fa fa-refresh"></i> <span data-i18n="ethip.endpoint.label.cycletime"></span></label>
				<input type="text" id="node-config-input-cycletime" data-i18n="[placeholder]ethip.endpoint.label.cycletime" style="width: 60px;"> <span>ms</span>
			</div>
			<div class="form-row">
				<label for="node-config-input-timeout"><i class="fa fa-refresh"></i> <span data-i18n="ethip.endpoint.label.timeout"></span></label>
				<input type="text" id="node-config-input-timeout" data-i18n="[placeholder]ethip.endpoint.label.timeout" style="width: 60px;"> <span>ms</span>
			</div>
			<div class="form-row">
				<label for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="ethip.label.name"></span></label>
				<input type="text" id="node-config-input-name" data-i18n="[placeholder]ethip.label.name">
			</div>
		</div>
		<div id="ethip-endpoint-tab-variables" style="display:none; height: 90%;">
			<div class="form-row eth-ip-vars-button-group" style="margin-bottom:0; height: 5%">
				<a href="#" class="editor-button editor-button-small eth-ip-vars-btn-clean" style="margin: 4px; float: right;"><i class="fa fa-trash-o"></i> <span data-i18n="ethip.endpoint.label.variables.clean"></span></a>
				<a href="#" class="editor-button editor-button-small eth-ip-vars-btn-add-prog" style="margin: 4px; float: right;"><i class="fa fa-plus"></i> <span data-i18n="ethip.endpoint.label.variables.add"></span></a>
				<a href="#" class="editor-button editor-button-small eth-ip-vars-btn-expand" style="margin: 4px; float: right;"><i class="fa fa-angle-double-down"></i></a>
				<a href="#" class="editor-button editor-button-small eth-ip-vars-btn-collapse" style="margin: 4px; float: right;"><i class="fa fa-angle-double-up"></i></a>
				<label><b><span data-i18n="ethip.endpoint.label.variables.list"></b></span></label>
			</div>
			<div class="form-row node-input-variables-container-row" style="margin-bottom: 0px; max-height: 100%; overflow-y: auto">
				<ol id="node-config-input-variables-container" style="min-height: 250px; min-width: 450px;"></ol>
			</div>
			<div class="form-row">
				<a href="#" class="editor-button editor-button-small eth-ip-vars-btn-export" style="margin: 4px; float: right"><i class="fa fa-download"></i> <span data-i18n="ethip.endpoint.label.variables.export"></span></a>
				<input type="file" id="node-config-ethip-endpoint-var-import" style="display: none"/>
				<a href="#" class="editor-button editor-button-small eth-ip-vars-btn-import" style="margin: 4px; float: right"><i class="fa fa-upload"></i> <span data-i18n="ethip.endpoint.label.variables.import"></span></a>
			</div>
		</div>
	</div>
</script>

<script type="text/javascript">
(function(){

	var saveAs = saveAs || function (e) { "use strict"; if (typeof e === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) { return } var t = e.document, n = function () { return e.URL || e.webkitURL || e }, r = t.createElementNS("http://www.w3.org/1999/xhtml", "a"), o = "download" in r, a = function (e) { var t = new MouseEvent("click"); e.dispatchEvent(t) }, i = /constructor/i.test(e.HTMLElement) || e.safari, f = /CriOS\/[\d]+/.test(navigator.userAgent), u = function (t) { (e.setImmediate || e.setTimeout)(function () { throw t }, 0) }, s = "application/octet-stream", d = 1e3 * 40, c = function (e) { var t = function () { if (typeof e === "string") { n().revokeObjectURL(e) } else { e.remove() } }; setTimeout(t, d) }, l = function (e, t, n) { t = [].concat(t); var r = t.length; while (r--) { var o = e["on" + t[r]]; if (typeof o === "function") { try { o.call(e, n || e) } catch (a) { u(a) } } } }, p = function (e) { if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)) { return new Blob([String.fromCharCode(65279), e], { type: e.type }) } return e }, v = function (t, u, d) { if (!d) { t = p(t) } var v = this, w = t.type, m = w === s, y, h = function () { l(v, "writestart progress write writeend".split(" ")) }, S = function () { if ((f || m && i) && e.FileReader) { var r = new FileReader; r.onloadend = function () { var t = f ? r.result : r.result.replace(/^data:[^;]*;/, "data:attachment/file;"); var n = e.open(t, "_blank"); if (!n) e.location.href = t; t = undefined; v.readyState = v.DONE; h() }; r.readAsDataURL(t); v.readyState = v.INIT; return } if (!y) { y = n().createObjectURL(t) } if (m) { e.location.href = y } else { var o = e.open(y, "_blank"); if (!o) { e.location.href = y } } v.readyState = v.DONE; h(); c(y) }; v.readyState = v.INIT; if (o) { y = n().createObjectURL(t); setTimeout(function () { r.href = y; r.download = u; a(r); h(); c(y); v.readyState = v.DONE }); return } S() }, w = v.prototype, m = function (e, t, n) { return new v(e, t || e.name || "download", n) }; if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) { return function (e, t, n) { t = t || e.name || "download"; if (!n) { e = p(e) } return navigator.msSaveOrOpenBlob(e, t) } } w.abort = function () { }; w.readyState = w.INIT = 0; w.WRITING = 1; w.DONE = 2; w.error = w.onwritestart = w.onprogress = w.onwrite = w.onabort = w.onerror = w.onwriteend = null; return m }(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content); if (typeof module !== "undefined" && module.exports) { module.exports.saveAs = saveAs } else if (typeof define !== "undefined" && define !== null && define.amd !== null) { define("FileSaver.js", function () { return saveAs }) }

	//["BOOL", "SINT", "INT", "DINT", "LINT", "USINT", "UINT", "UDINT", "REAL", "LREAL", "STIME", "DATE", "TIME_AND_DAY", "DATE_AND_STRING", "STRING", "WORD", "DWORD", "BIT_STRING", "LWORD", "STRING2", "FTIME", "LTIME", "ITIME", "STRINGN", "SHORT_STRING", "TIME", "EPATH", "ENGUNIT", "STRINGI", "STRUCT"];
	var dataTypes = ["", "BOOL", "SINT", "INT", "DINT", "REAL"];

	function debuglog(){
		//console.log(arguments);
	}

	function generateVarTable(){
		var vartable = {};
		var progItems = $("#node-config-input-variables-container").editableList('items');

		progItems.each(function (i){
			var progItem = $(this);
			var progName = progItem.find('.eth-ip-vars-prog-name').val() || '';

			vartable[progName] = {};

			var varItems = progItem.find('.eth-ip-vars-var-list').editableList('items');
			varItems.each(function (j){
				var varItem = $(this);
				var varName = varItem.find('.eth-ip-vars-var-name').val();
				var varType = varItem.find('.eth-ip-vars-var-type').val();

				vartable[progName][varName] = {
					type: varType
				};
			});
		});

		debuglog('generateVarTable', vartable);

		return vartable;
	}
	
	RED.nodes.registerType('eth-ip endpoint', {
		category: 'config',
		defaults: {
			address: {
				value: "",
				validate: RED.validators.regex(/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/)
			},
			slot: {
				value: "0",
				validate: RED.validators.number()
			},
			cycletime: {
				value: 3000
			},
			timeout: {
				value: 10000
			},
			name: {
				value: ""
			},
			vartable: {
				value: {
					'': {
						'': {
							type: ''
						}
					}
				}
			}
		},
		label: function() {
			return this.name || this.address + ":" + this.slot;
		},
		oneditprepare: function() {
			var self = this;

			//labels (i18n)
			var labelName = this._("ethip.endpoint.label.variables.name");
			var labelAddr = this._("ethip.endpoint.label.variables.addr");
			var labelAdd = this._("ethip.endpoint.label.variables.add");
			var labelDel = this._("ethip.endpoint.label.variables.del");
			var labelGlobal = this._("ethip.endpoint.label.variables.global");
			var labelProgram = this._("ethip.endpoint.label.variables.program");
			var labelTag = this._("ethip.endpoint.label.variables.tag");

			//elms
			var progContainer = $("#node-config-input-variables-container");
			var btnCollapse = $('.eth-ip-vars-btn-collapse');
			var btnExpand = $('.eth-ip-vars-btn-expand');
			var btnAddProg = $('.eth-ip-vars-btn-add-prog');
			var btnClean = $('.eth-ip-vars-btn-clean');
			var btnImport = $('.eth-ip-vars-btn-import');
			var btnExport = $('.eth-ip-vars-btn-export');

			// Prepare tabs
			var tabs = RED.tabs.create({
				id: "node-config-ethip-endpoint-tabs",
				onchange: function(tab) {
					$("#node-config-ethip-endpoint-tabs-content").children().hide();
					$("#" + tab.id).show();
				}
			});
			tabs.addTab({
				id: "ethip-endpoint-tab-connection",
				label: this._("ethip.endpoint.label.tabs.connection")
			});
			tabs.addTab({
				id: "ethip-endpoint-tab-variables",
				label: this._("ethip.endpoint.label.tabs.variables")
			});
			setTimeout(function() {
				tabs.resize()
			}, 0);

			function cleanTable(skipGlobal){
				progContainer.empty();
				if(skipGlobal) return;
				progContainer.editableList('addItem', {name: '', isGlobal: true, vars: {}});
			}

			// export
			function exportCSV() {

				var vars = generateVarTable();
				var lines = [];

				Object.keys(vars).forEach(function (progName){
					Object.keys(vars[progName]).forEach(function (varName) {
						var obj = vars[progName][varName];

						var line = [progName, varName, obj.type];
						lines.push(line.join(';'));
					});
				});

				saveAs(new Blob([lines.join('\r\n')]), 'ethip-endpoint' + (self.name ? '_' + self.name : '') + '.csv');
			}

			// import
			function importCSV(e) {

				var file = e.target.files[0];
				if (!file) {
					return;
				}
				var reader = new FileReader();
				reader.onload = function (e) {
					var res = {}, i, fields, resKeys;
					var contents = e.target.result || '';
					var lines = contents.split(/[\r\n]+/);

					if (!lines.length) {
						alert('file is empty!');
						return;
					}

					debuglog('importCSV lines', lines);

					for (i = 0; i < lines.length; i++) {

						lines[i] = lines[i].trim();
						if (lines[i] == '') continue;

						fields = lines[i].split(/[\t;]/);

						if (fields.length < 2) {
							alert('line must have at least two parameters, program and tag name');
							return;
						}

						res[fields[0]] = res[fields[0]] || {};
						res[fields[0]][fields[1]] = {
							type: fields[2]
						}
					}

					debuglog('importCSV result', res)

					resKeys = Object.keys(res);
					if (resKeys.length) {
						cleanTable(true);

						resKeys.forEach(function(elm){
							progContainer.editableList('addItem', {name: elm, isGlobal: elm === '', vars: res[elm]});
						});
					}
				};
				reader.readAsText(file);
			}

			// Buttons
			btnCollapse.click(function(evt) {
				//TODO replace with the right selectors
				progContainer.find(".eth-ip-vars-var-container").slideUp();
				progContainer.find(".eth-ip-vars-prog-title>.eth-ip-vars-prog-chevron").css({ "transform": "rotate(-90deg)" });
				evt.preventDefault();
			});
			btnExpand.click(function(evt) {
				//TODO replace with the right selectors
				progContainer.find(".eth-ip-vars-var-container").slideDown();
				progContainer.find(".eth-ip-vars-prog-title>.eth-ip-vars-prog-chevron").css({ "transform": "" });
				evt.preventDefault();
			});
			btnAddProg.click(function(evt) {
				progContainer.editableList('addItem', {});
				evt.preventDefault();
			});
			btnClean.click(function(evt) {
				cleanTable();
				evt.preventDefault();
			});
			btnExport.click(exportCSV);
			btnImport.click(function(evt) {
				$('#node-config-ethip-endpoint-var-import').click();
			});
			$('#node-config-ethip-endpoint-var-import').on('change', importCSV);

			// toggle slide tab group content
			var titleToggle = function (content, chevron) {
				return function (evt) {
					if (content.is(":visible")) {
						content.slideUp();
						chevron.css({ "transform": "rotate(-90deg)" });
						//content.addClass('nr-db-sb-collapsed');
					}
					else {
						content.slideDown();
						chevron.css({ "transform": "" });
						//content.removeClass('nr-db-sb-collapsed');
					}
				};
			};

			progContainer.editableList({
				sortable: ".eth-ip-vars-prog-handle",
				//removable: "eth-ip-vars-prog-btn-del",
				removable: false,
				addButton: false,
				addItem: function (container, i, opts){
					container.addClass('eth-ip-vars-prog-item');

					var titleRow = $('<div/>', {class:"eth-ip-vars-prog-title"}).appendTo(container);

					var handle = $('<i class="fa fa-bars eth-ip-vars-prog-handle"/>').appendTo(titleRow);
					var chevron = $('<i class="fa fa-angle-down eth-ip-vars-prog-chevron"/>').appendTo(titleRow);
					var progName;
					if(opts.isGlobal){
						progName = $('<span style=""/>').text(labelGlobal).appendTo(titleRow);
					} else {
						progName = $('<input/>',{type: "text", placeholder: labelProgram, class: "eth-ip-vars-prog-name"}).appendTo(titleRow);
						progName.val(opts.name);
					}

					var buttonGroup = $('<div/>', {style:"float: right"}).appendTo(titleRow);
					var buttonAddVar = $('<a href="#" style="margin: auto 4px" class="editor-button editor-button-small"><i class="fa fa-plus"></i> ' + labelAdd + '</a>').appendTo(buttonGroup);
					var buttonDelProg = $('<a href="#" style="margin: auto 4px" class="editor-button editor-button-small eth-ip-vars-prog-btn-del"><i class="fa fa-remove"></i></a>').appendTo(buttonGroup);
					if(opts.isGlobal){
						buttonDelProg.hide();
					}

					//--

					var contentRow = $('<div/>', {class:"eth-ip-vars-var-container"}).appendTo(container);

					var ol = $('<ol>', {class:"eth-ip-vars-var-list"}).appendTo(contentRow).editableList({
						addButton: false,
						height: 'auto',
						connectWith: '.eth-ip-vars-var-list',
						removable: true,
						sortable: true,
						addItem: function (container, i, opts) {
							container.addClass('eth-ip-vars-var-item');

							var varName = $('<input/>', {type:"text", placeholder: labelTag, class:"eth-ip-vars-var-name"}).appendTo(container);
							var varType = $('<select/>', {class: "eth-ip-vars-var-type"}).appendTo(container);
							for (var i in dataTypes) {
								$('<option></option>').val(dataTypes[i]).text(dataTypes[i]).appendTo(varType);
							}

							varName.val(opts.name);
							varType.val(opts.data ? opts.data.type : undefined);
						}
					});

					//--

					buttonAddVar.click(function (evt) {
						ol.editableList('addItem', {});
						evt.stopPropagation();
						evt.preventDefault();
					});
					buttonDelProg.click(function (evt) {
						progContainer.editableList('removeItem', container.data('data'));
						evt.stopPropagation();
						evt.preventDefault();
					});
					chevron.click(titleToggle(contentRow, chevron));

					Object.keys(opts.vars || {}).forEach(function (elm) {
						ol.editableList('addItem', {name: elm, data: opts.vars[elm]});
					});
				}
			});

			Object.keys(self.vartable).forEach(function (elm) {
				progContainer.editableList('addItem', {name: elm, isGlobal: elm === '', vars: self.vartable[elm]});
			})

			// ----------------------------------------

			$("#node-config-input-cycletime").spinner({
				min: 50
			});

			$("#node-config-input-timeout").spinner({
				min: 1000
			});

			if (self.timeout === undefined) {
				self.timeout = 10000;
				$("#node-config-input-timeout").val(10000);
			}
		},
		oneditsave: function() {
			var node = this;
			node.vartable = generateVarTable();
		}
	});
})();
</script>

<!-- ######################################################################################## -->

<script type="text/html" data-template-name="eth-ip in">
	<div class="form-row">
		<label for="node-input-endpoint"><i class="fa fa-bolt"></i> <span data-i18n="ethip.in.label.endpoint"></span></label>
		<input type="text" id="node-input-endpoint" data-i18n="[placeholder]ethip.in.label.endpoint">
	</div>
	<div class="form-row">
		<label for="node-input-mode"><i class="fa fa-sliders"></i> <span data-i18n="ethip.in.label.mode"></span></label>
		<select type="text" id="node-input-mode">
			<option value="single" data-i18n="ethip.in.mode.single"></option>
			<option value="all-split" data-i18n="ethip.in.mode.all-split"></option>
			<option value="all" data-i18n="ethip.in.mode.all"></option>
		</select>
	</div>
	<div class="form-row ethip-input-var-row">
		<label for="node-input-program"><i class="fa fa-random"></i> <span data-i18n="ethip.in.label.program"></span></label>
		<select type="text" id="node-input-program">
		</select>
	</div>
	<div class="form-row ethip-input-var-row">
		<label for="node-input-variable"><i class="fa fa-random"></i> <span data-i18n="ethip.in.label.variable"></span></label>
		<select type="text" id="node-input-variable">
		</select>
	</div>
	<div class="form-row">
		<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="ethip.label.name"></span></label>
		<input type="text" id="node-input-name" data-i18n="[placeholder]ethip.label.name">
	</div>
</script>

<script type="text/javascript">
	RED.nodes.registerType('eth-ip in', {
		category: 'plc',
		defaults: {
			endpoint: {
				value: "",
				type: "eth-ip endpoint",
				required: true
			},
			mode: {
				value: "single"
			},
			variable: {
				value: "",
				validate: function (value) {
					return this.mode == "single" ? !!value : true;
				}
			},
			program: {
				value: ""
			},
			name: {
				value: ""
			}
		},
		color: "#D19BA1",
		inputs: 0,
		outputs: 1,
		icon: "serial.png",
		label: function() {
			if (this.name) return this.name;

			var endpointNode = RED.nodes.node(this.endpoint);
			if (endpointNode) {
				if (this.mode != 'single') {
					return endpointNode.label();
				}
				if (this.program) {
					return this.program + " / " + this.variable;
				}
				return this.variable || endpointNode.label();
			}
			return this._("ethip.in.label.name");
		},
		labelStyle: function() {
			return this.name ? "node_label_italic" : "";
		},
		oneditprepare: function () {
			var self = this;

			var progList = $('#node-input-program');
			var varList = $('#node-input-variable');
			var modeList = $('#node-input-mode');
			var endpointList = $("#node-input-endpoint");
			var varDivs = $('.ethip-input-var-row');
			var vars = {};

			function cleanupLists(cleanProg) {
				$('#node-input-variable option').remove();
				varList.append($('<option/>', {
					disabled: "disabled",
					selected: "selected",
					style: "display:none;",
					text: self._("ethip.in.label.variable-select"),
					value: ""
				}));

				if (!cleanProg) return;

				$('#node-input-program option').remove();
			}

			function updateProgList(endpointId) {
				//cleanup selects
				cleanupLists(true);

				var endpointNode = RED.nodes.node(endpointId);
				if (!endpointNode) return;
				vars = endpointNode.vartable || {};

				Object.keys(vars).forEach(function (elm) {
					var txt = elm === "" ? self._("ethip.out.label.global") : elm;
					progList.append($('<option/>', {
						text: txt,
						value: elm
					}));
					if (elm === self.program) {
						progList.val(self.program);
						updateVarList(elm);
					}
				});
			}

			function updateVarList(progName) {
				cleanupLists(false);

				if(!vars[progName]) return;

				Object.keys(vars[progName]).forEach(function (elm) {
					varList.append($('<option/>', {
						value: elm,
						text: elm
					}));
					if (elm == self.variable) {
						varList.val(self.variable);
					}
				});
			}

			progList.change(function () {
				updateVarList(progList.val());
			});
			endpointList.change(function () {
				updateProgList(endpointList.val());
			});
			updateProgList(self.endpoint);

			modeList.change(function () {
				if (modeList.val() == "single") {
					varDivs.show();
				} else {
					varDivs.hide();
				}
			});
			modeList.change();
		},
		oneditsave: function() {
			var endpointNode = RED.nodes.node($("#node-input-endpoint").val());
			var progValue = $('#node-input-program').val();
			var varValue = $('#node-input-variable').val();

			if(!endpointNode) return;

			//validate: cleanup fields if they don't exist on the endpoint 
			if(!endpointNode.vartable[progValue]){
				this.program = "";
				this.variable = "";
			} else if (!endpointNode.vartable[progValue][varValue]) {
				this.variable = "";
			}
		}
	});
</script>

<!-- ######################################################################################## -->

<script type="text/html" data-template-name="eth-ip out">
	<div class="form-row">
		<label for="node-input-endpoint"><i class="fa fa-bolt"></i> <span data-i18n="ethip.out.label.endpoint"></span></label>
		<input type="text" id="node-input-endpoint" data-i18n="[placeholder]ethip.out.label.endpoint">
	</div>
	<div class="form-row">
		<label for="node-input-program"><i class="fa fa-random"></i> <span data-i18n="ethip.out.label.program"></span></label>
		<select type="text" id="node-input-program">
		</select>
	</div>
	<div class="form-row">
		<label for="node-input-variable"><i class="fa fa-random"></i> <span data-i18n="ethip.out.label.variable"></span></label>
		<select type="text" id="node-input-variable">
		</select>
	</div>
	<div class="form-row">
		<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="ethip.label.name"></span></label>
		<input type="text" id="node-input-name" data-i18n="[placeholder]ethip.label.name">
	</div>
	<div class="form-tips"><span data-i18n="[html]ethip.out.disclaimer"></span></div>
</script>

<script type="text/javascript">
	RED.nodes.registerType('eth-ip out', {
		category: 'plc',
		defaults: {
			endpoint: {
				value: "",
				type: "eth-ip endpoint",
				required: true
			},
			variable: {
				value: "",
				validate: function(value) { this.program ? this.value : true }
			},
			program: {
				value: ""
			},
			name: {
				value: ""
			}
		},
		color: "#D19BA1",
		inputs: 1,
		outputs: 0,
		icon: "serial.png",
		align: 'right',
		label: function() {
			if (this.name) return this.name;

			var endpointNode = RED.nodes.node(this.endpoint);
			if (endpointNode) {
				if (this.program) {
					return this.program + " / " + this.variable;
				}
				return this.variable || endpointNode.label();
			}
			return this._("ethip.out.label.name");
		},
		labelStyle: function() {
			return this.name ? "node_label_italic" : "";
		},
		oneditprepare: function() {
			var self = this;

			var progList = $('#node-input-program');
			var varList = $('#node-input-variable');
			var endpointList = $("#node-input-endpoint");
			var vars = {};

			function cleanupLists(cleanProg){
				$('#node-input-variable option').remove();
				varList.append($('<option/>', {
					disabled: "disabled",
					selected: "selected",
					style: "display:none;",
					text: self._("ethip.out.label.variable-select"),
					value: ""
				}));

				if(!cleanProg) return;

				$('#node-input-program option').remove();
			}

			function updateProgList(endpointId) {
				//cleanup selects
				cleanupLists(true);
				
				var endpointNode = RED.nodes.node(endpointId);
				if (!endpointNode) return;
				vars = endpointNode.vartable || {};
				
				Object.keys(vars).forEach(function (elm) {
					var txt = elm === "" ? self._("ethip.out.label.global") : elm;
					progList.append($('<option/>', {
						text: txt,
						value: elm
					}));
					if(elm === self.program){
						progList.val(self.program);
						updateVarList(elm);
					}
				});
			}

			function updateVarList(progName) {
				cleanupLists(false);
				if (progList.val() == "") {
					varList.append($('<option/>', {
						value: "",
						text: ""
					}));
				}

				if(!vars[progName]) return;

				Object.keys(vars[progName]).forEach(function (elm){
					varList.append($('<option/>', {
						value: elm,
						text: elm
					}));
					if(elm == self.variable) {
						varList.val(self.variable);
					}
				});
			}

			progList.change(function(){
				updateVarList(progList.val());
			});
			endpointList.change(function() {
				updateProgList(endpointList.val());
			});
			updateProgList(self.endpoint);
		},
		oneditsave: function () {
			var endpointNode = RED.nodes.node($("#node-input-endpoint").val());
			var progValue = $('#node-input-program').val();
			var varValue = $('#node-input-variable').val();

			if (!endpointNode) return;

			//validate: cleanup fields if they don't exist on the endpoint 
			if (!endpointNode.vartable[progValue]) {
				this.program = "";
				this.variable = "";
			} else if (!endpointNode.vartable[progValue][varValue]) {
				this.variable = "";
			}
		}
	});
</script>